package org.msfj.common.util;
import org.bouncycastle.crypto.digests.Blake2bDigest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;

/**
 * @Description:
 * @Author: ..
 * @Date: 2018-03-20 10:49
 * @Version: 1.0
 * @Created in idea by autoCode
 */
public class SignatureUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(SignatureUtils.class);
    private static final Charset DefaultCharset = StandardCharsets.UTF_8;

    public enum SupportedSignatureAlgorithm {
        BLAKE2BwithNONE,
        BLAKE2BwithRSA,
        NONEwithRSA
    }

    private static byte[] sign(String algorithm, PrivateKey privk, byte[] bytes) throws Exception {
        Signature sig = Signature.getInstance(algorithm);
        sig.initSign(privk);
        sig.update(bytes);
        return sig.sign();
    }

    private static byte[] sign(SupportedSignatureAlgorithm alg, PrivateKey privk, byte[] bytes)
            throws Exception {
        String algn;
        switch (alg) {
            case BLAKE2BwithNONE:
                return hashBlake2b(bytes);

            case BLAKE2BwithRSA:
                bytes = hashBlake2b(bytes);
                algn = SupportedSignatureAlgorithm.NONEwithRSA.name();
                break;

            default:
                algn = alg.name();
        }
        return sign(algn, privk, bytes);
    }

    private static byte[] hashBlake2b(byte[] bytes) {
        Blake2bDigest b2bd = new Blake2bDigest(512);
        b2bd.update(bytes, 0, bytes.length);
        byte[] ret = new byte[1024];
        int len = b2bd.doFinal(ret, 0);
        return Arrays.copyOfRange(ret, 0, len);
    }

    private static byte[] map2SortedMapBytes(Map<?, ?> map) {
        Map<?, ?> sm = new TreeMap<>(map);
        StringBuilder sb = new StringBuilder();
        boolean firstEntry = true;
        for (Map.Entry<?, ?> entry : sm.entrySet()) {
            if (!firstEntry) {
                sb.append("&");
            } else {
                firstEntry = false;
            }
            sb.append(entry.getKey()).append("=").append(entry.getValue());
        }
        return sb.toString().getBytes(DefaultCharset);
    }

    private static byte[] sign(SupportedSignatureAlgorithm alg, PrivateKey privk, Map<?, ?> map)
            throws Exception {
        return sign(alg, privk, map2SortedMapBytes(map));
    }

    public static byte[] getSig(Map<?, ?> map) {
        try {
            byte[] sigByte = SignatureUtils.sign(SignatureUtils.SupportedSignatureAlgorithm.BLAKE2BwithNONE, null, map);
            return sigByte; //Base64.encodeBase64String(sigByte);
        } catch (Exception e) {
            LOGGER.error("black2b getsign exception:", e);
        }
        return null;
    }

    public  boolean compareSign(Map<?, ?> map, byte[] dst) {
        //return sigText.equals(getSig(map));
        return Arrays.equals(getSig(map), dst);
    }

    //测试用
   /* public static void main(String[] args) {
        Map<String, Object> sigMap = new HashMap<>();
        sigMap.put("cert_token", "xxx");
        sigMap.put("full_name", "xxx");
        sigMap.put("id_num", "xxx");
        sigMap.put("cert_res", 0);
        sigMap.put("cert_mode", 66);
        sigMap.put("client_id", "xxx");
        sigMap.put("client_secret", "xxx");
        System.err.println(compareSign(sigMap, Base64.decodeBase64("fOfgukc/e+vV2ifW6u/xVWNDGSnURxLg3XI9aWzF4d4ekZOyVEzCY8K8z/PwOEMh4yP077kx/jq7o2xldqF3AQ==")));
    }*/
}

